

#ifndef _B5M_SINGLETON_H_
#define _B5M_SINGLETON_H_

#include "common.h"
#include "B5M_Object_Manager.h"
#include "B5M_Thread_Mutex.h"
#include "B5m_Guard.h"

B5M_BEGIN_VERSIONED_NAMESPACE_DECL
/**
 * @class B5M_Singleton
 *
 * @brief A Singleton Adapter uses the Adapter pattern to turn ordinary
 * classes into Singletons optimized with the Double-Checked
 * Locking optimization pattern.
 *
 * This implementation is a slight variation on the GoF
 * Singleton pattern.  In particular, a single
 * <B5M_Singleton<TYPE, B5M_LOCK> > instance is allocated here,
 * not a <TYPE> instance.  The reason for this is to allow
 * registration with the B5M_Object_Manager, so that the
 * Singleton can be cleaned up when the process exits.  For this
 * scheme to work, a (static) cleanup() function must be
 * provided.  B5M_Singleton provides one so that TYPE doesn't
 * need to.
 * If you want to make sure that only the singleton instance of
 * <T> is created, and that users cannot create their own
 * instances of <T>, do the following to class <T>:
 * (a) Make the constructor of <T> private (or protected)
 * (b) Make Singleton a friend of <T>
 * Here is an example:
 * @verbatim
 * class foo
 * {
 * friend class B5M_Singleton<foo, B5M_Null_Mutex>;
 * private:
 * foo () { cout << "foo constructed" << endl; }
 * ~foo () { cout << "foo destroyed" << endl; }
 * };
 * typedef B5M_Singleton<foo, B5M_Null_Mutex> FOO;
 * @endverbatim
 *
 * @note The best types to use for B5M_LOCK are
 * B5M_Recursive_Thread_Mutex and B5M_Null_Mutex.
 * B5M_Recursive_Thread_Mutex should be used in multi-threaded
 * programs in which it is possible for more than one thread to
 * access the <B5M_Singleton<TYPE, B5M_LOCK>> instance.
 * B5M_Null_Mutex can be used otherwise.  The reason that these
 * types of locks are best has to do with their allocation by
 * the B5M_Object_Manager.  Single B5M_Recursive_Thread_Mutex
 * and B5M_Null_Mutex instances are used for all B5M_Singleton
 * instantiations.  However, other types of locks are allocated
 * per B5M_Singleton instantiation.
 */

template <class TYPE, class B5M_LOCK = B5M_Thread_Mutex>
class B5M_Singleton : public B5M_Cleanup
{
public:
  /// Global access point to the Singleton.
  static TYPE *instance (void)
  {	  
	  //B5M_TRACE ("B5M_Singleton<TYPE, B5M_LOCK>::instance");

	  B5M_Singleton<TYPE, B5M_LOCK> *&singleton =
		  B5M_Singleton<TYPE, B5M_LOCK>::instance_i ();
	  // Perform the Double-Check pattern...
 	  if (singleton == 0)
 	  {		  

		  if (B5M_Object_Manager::starting_up () ||
			  B5M_Object_Manager::shutting_down ())
		  {
			  // The program is still starting up, and therefore assumed
			  // to be single threaded.  There's no need to double-check.
			  // Or, the B5M_Object_Manager instance has been destroyed,
			  // so the preallocated lock is not available.  Either way,
			  // don't register for destruction with the
			  // B5M_Object_Manager:  we'll have to leak this instance.

			  singleton = new B5M_Singleton<TYPE, B5M_LOCK>();
		  }
		  else
		  {
			  // Obtain a lock from the ACE_Object_Manager.  The pointer
			  // is static, so we only obtain one per ACE_Singleton
			  // instantiation.
			  static B5M_LOCK *lock = 0;
			  if (B5M_Object_Manager::get_singleton_lock (lock) != 0)
				  // Failed to acquire the lock!
				  return 0;

			  B5M_GUARD_RETURN (B5M_LOCK, ace_mon, *lock, 0);

			  if (singleton == 0)
			  {

				  B5M_NEW_RETURN (singleton, (B5M_Singleton<TYPE, B5M_LOCK>), 0);

				  // Register for destruction with ACE_Object_Manager.
				  B5M_Object_Manager::at_exit (singleton, 0, typeid (TYPE).name ());
			  }
		  }		
	  }

	 return &singleton->instance_;
  }
 
  /// Cleanup method, used by @c ace_cleanup_destroyer to destroy the
  /// ACE_Singleton.
  virtual void cleanup (void *param = 0)
  {
	  B5M_Object_Manager::remove_at_exit (this);
	  delete this;
	  B5M_Singleton<TYPE, B5M_LOCK>::instance_i () = 0;
  }

  /// Explicitly delete the Singleton instance.
  static void close (void)
  {
	  B5M_Singleton<TYPE, B5M_LOCK> *&singleton =
		  B5M_Singleton<TYPE, B5M_LOCK>::instance_i ();

	  if (singleton)
	  {
		  singleton->cleanup ();
		  B5M_Singleton<TYPE, B5M_LOCK>::instance_i () = 0;
	  }
  }

protected:
  /// Default constructor.
  B5M_Singleton (void){};

  /// Contained instance.
  TYPE instance_;

  /// Get pointer to the Singleton instance.
  static B5M_Singleton<TYPE, B5M_LOCK> *&instance_i (void)
  {
	  // Pointer to the Singleton instance.  This works around a bug with
	  // G++ and it's (mis-)handling of templates and statics...
	  static B5M_Singleton<TYPE, B5M_LOCK> *singleton_ = 0;

	  return singleton_;
  }
};

B5M_END_VERSIONED_NAMESPACE_DECL

#endif